package com.mhc.haval.config;

import com.mhc.haval.security.filter.CustomAuthenticationFilter;
import com.mhc.haval.security.filter.CustomExceptionTranslationFilter;
import com.mhc.haval.security.handler.CookieHeaderHttpSessionStrategy;
import com.mhc.haval.security.handler.CustomAuthenticationSuccessHandler;
import com.mhc.haval.security.service.impl.SecurityUserDetailService;
import com.subaru.common.util.MD5Util;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.ServletListenerRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.session.SessionRegistry;
import org.springframework.security.core.session.SessionRegistryImpl;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.access.ExceptionTranslationFilter;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.session.HttpSessionEventPublisher;
import org.springframework.session.FindByIndexNameSessionRepository;
import org.springframework.session.security.SpringSessionBackedSessionRegistry;
import org.springframework.session.web.http.HttpSessionStrategy;

/**
 * @author Churry
 * @create 2017-08-15 15:45
 **/
@EnableWebSecurity
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    @SuppressWarnings("SpringJavaAutowiringInspection")
    private FindByIndexNameSessionRepository sessionRepository;

    //springsession使用token与cookie结合的方式
    @Bean
    public HttpSessionStrategy httpSessionStrategy() {
        return new CookieHeaderHttpSessionStrategy();
    }

    @Override
    public void configure(WebSecurity security) {
        //此配置无效，尚不清楚原因
        //security.ignoring().antMatchers("/static/**", "/static/**/**", "/swagger-ui");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .authorizeRequests()
                .antMatchers("/", "/login", "/logout", "/static/**", "/v2/api-docs", "/webjars/**", "/swagger-resources/**", "/swagger-ui.html", "/accessAllowedRole/list", "/feign/**").permitAll()
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .loginPage("/")
                .loginProcessingUrl("/login")
                .failureUrl("/loginError")
                .defaultSuccessUrl("/index")
                .and()
                .sessionManagement()
                //防止session固定攻击，策略为改变sessionid
                .sessionFixation().changeSessionId()
                //允许一个用户同时存在的session数量，超过时阻止登录，但目前使用springsession后此配置无效
                .maximumSessions(1).maxSessionsPreventsLogin(true)
                //注册session管理
                .sessionRegistry(sessionRegistry())
                .and()
                .and()
                .logout()
                .logoutUrl("/logout")
                .logoutSuccessUrl("/")
                .invalidateHttpSession(true)
                .clearAuthentication(true)
                .and()
                //用重写的Filter替换掉原有的UsernamePasswordAuthenticationFilter
                .addFilterAt(customAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
                //加入重写的认证失败处理类CustomExceptionTranslationFilter
                .addFilterAfter(customExceptionTranslationFilter(), ExceptionTranslationFilter.class)
                .csrf().disable();
    }

    //注册UserDetailsService 的bean
    @Bean
    UserDetailsService userDetailService() {
        return new SecurityUserDetailService();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailService()).passwordEncoder(new PasswordEncoder() {
            @Override
            public String encode(CharSequence rawPassword) {
                return MD5Util.stringMD5((String) rawPassword);
            }

            @Override
            public boolean matches(CharSequence rawPassword, String encodedPassword) {
                String encodeRawPassword = MD5Util.stringMD5((String) rawPassword);
                return encodedPassword.equals(encodeRawPassword);
            }
        });
    }

    //使用springsession的session管理
    @Bean
    @SuppressWarnings("unchecked")
    public SpringSessionBackedSessionRegistry sessionRegistry() {
        return new SpringSessionBackedSessionRegistry(this.sessionRepository);
    }

    // Register HttpSessionEventPublisher
    @Bean
    @SuppressWarnings("unchecked")
    public static ServletListenerRegistrationBean httpSessionEventPublisher() {
        return new ServletListenerRegistrationBean(new HttpSessionEventPublisher());
    }

    //注册自定义的UsernamePasswordAuthenticationFilter
    @Bean
    CustomAuthenticationFilter customAuthenticationFilter() throws Exception {
        CustomAuthenticationFilter filter = new CustomAuthenticationFilter();
        filter.setAuthenticationSuccessHandler(new CustomAuthenticationSuccessHandler());
        filter.setFilterProcessesUrl("/login");
        //这句很关键，重用WebSecurityConfigurerAdapter配置的AuthenticationManager，不然要自己组装AuthenticationManager
        filter.setAuthenticationManager(authenticationManagerBean());
        return filter;
    }

    //注册自定义令牌过期处理类
    @Bean
    CustomExceptionTranslationFilter customExceptionTranslationFilter() {
        return new CustomExceptionTranslationFilter();
    }

}
